﻿using DevExpress.XtraBars;
using DevExpress.XtraEditors;
using DevExpress.XtraEditors.Container;
using DevExpress.XtraLayout;
using DevExpress.XtraTreeList;
using Inaction.ServiceContract;
using Inaction.Core;
using Inaction.Mapping;
using Inaction.UI.Common;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

namespace Inaction.UI
{
    public class FormManager
    {
        #region 属性/变量
        private Form form = null;

        public Form Form
        {
            get { return form; }
        }

        public Dictionary<BindingSource, List<EditorContainer>> BindingEditorContainers { get; private set; }
        public Dictionary<BindingSource, SortedEditorList> BindingEditors { get; private set; }
        public Dictionary<BindingSource, SortedEditorList> CriteriaBindingEditors { get; private set; }
        public List<Control> LayoutControls { get; private set; }
        public List<Control> Controls { get; private set; }
        public List<EditorContainer> EditorContainers { get; private set; }
        public List<BaseEdit> BaseEdits { get; private set; }
        internal BarManager BarManager { get; private set; }

        BaseEdit focusControl;

        #endregion

        #region 构造
        public FormManager(Form form, BarManager barManager)
        {
            if (barManager.IsDesignMode) return;
            this.form = form;
            try
            {
                this.BarManager = barManager;
                BindingEditorContainers = new Dictionary<BindingSource, List<EditorContainer>>();
                BindingEditors = new Dictionary<BindingSource, SortedEditorList>();
                CriteriaBindingEditors = new Dictionary<BindingSource, SortedEditorList>();
                LayoutControls = new List<Control>();
                EditorContainers = new List<EditorContainer>();
                BaseEdits = new List<BaseEdit>();
                Controls = Inaction.UI.Common.ControlHelper.FindControls<Control>(form).ToList();
                GroupControls(Controls);
                SetEditorContainer(EditorContainers);
                SetBaseEdit(BaseEdits);
                form.Load += (sender, e) =>
                {
                    ApplyFormAuthorization();
                };
            }
            catch { }
        }

        #endregion

        #region 方法
        static Dictionary<Type, List<string>> FormDenyExecutes = new Dictionary<Type, List<string>>();
        internal bool IsAllowExecute(string componentName)
        {
            return true;
        }
        private void ApplyFormAuthorization()
        {
            if (Inaction.Security.Principal.Instance.Identity.IsAdmin) return;
            List<string> list = null;
            if (FormDenyExecutes.ContainsKey(form.GetType()))
            {
                list = FormDenyExecutes[form.GetType()];
            }
            if (list == null || list.Count == 0) return;
            for (int i = 0; i < list.Count; i++)
            {
                var name = list[i];
                var field = form.GetType().GetField(name, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public);
                if (field == null)
                {
                    list.Remove(name);
                    continue;
                }
                var c = field.GetValue(form) as Component;
                if (c == null)
                {
                    list.Remove(name);
                    continue;
                }
                if (c is Control) { (c as Control).Enabled = false; }
                else if (c is Component)
                {
                    if (c is BarItem)
                    {
                        var barItem = c as BarItem;
                        barItem.Enabled = false;
                    }
                    else if (c is MenuItem)
                    {
                        (c as MenuItem).Enabled = false;
                    }
                }
            }
        }
        private void GroupControls(IEnumerable<Control> controls)
        {
            foreach (var control in controls)
            {
                #region LayoutControls
                if (LayoutHelper.IsLayoutControl(control))
                {
                    LayoutControls.Add(control);
                }
                #endregion

                if (control is EditorContainer)
                {
                    EditorContainers.Add(control as EditorContainer);
                }
                if (control is BaseEdit)
                {
                    var baseEdit = control as BaseEdit;
                    BaseEdits.Add(baseEdit);
                }
            }
        }
        private void SetEditorContainer(IEnumerable<Control> controls)
        {
            if (Form.IsDisposed) return;
            if (BarManager == null) return;
            var editorContainers = controls.Where(p => p is EditorContainer).Cast<EditorContainer>().ToList();
            foreach (var editorContainer in editorContainers)
            {
                var dataSource = editorContainer.GetType().GetProperty("DataSource").GetValue(editorContainer, null);
                var bindingSource = dataSource as BindingSource;
                if (bindingSource == null) continue;
                var bindingInfo = BarManager.BindingInfos.FirstOrDefault(p => p.BindingSource == bindingSource);
                if (bindingInfo == null) continue;
                List<EditorContainer> controlList = null;
                if (!BindingEditorContainers.ContainsKey(bindingSource))
                {
                    BindingEditorContainers.Add(bindingSource, new List<EditorContainer>());
                }
                controlList = BindingEditorContainers[bindingSource];

                #region GotFocus时切换当前数据源(CurrentInfo)
                editorContainer.GotFocus += (sender, e) =>
                {
                    foreach (var item in BindingEditorContainers)
                    {
                        if (item.Value.Contains((Control)sender))
                        {
                            var b = BarManager.BindingInfos.FirstOrDefault(p => p.BindingSource == item.Key);
                            if (BarManager.CurrentBindingInfo != b)
                            {
                                BarManager.CurrentBindingInfo = b;
                                break;
                            }
                        }
                    }
                    BarManager.focusedEditorContainer = sender as EditorContainer;
                };
                #endregion

                if (editorContainer is TreeList)
                {
                    var treeList = editorContainer as TreeList;
                    treeList.FocusedNodeChanged += (s1, args1) =>
                    {
                        var bindingEditorContainer = BindingEditorContainers.FirstOrDefault(p => p.Value.Contains(treeList));
                        bindingEditorContainer.Key.Position = bindingEditorContainer.Key.IndexOf(treeList.GetDataRecordByNode(args1.Node));
                    };
                }

                if (!controlList.Contains(editorContainer))
                {
                    controlList.Add(editorContainer);
                    editorContainer.GotFocus += ControlGotFocus;
                }
            }
        }
        Control focusedControl = null;
        void ControlGotFocus(object sender, EventArgs e)
        {
            if (focusedControl != null)
            {
                focusedControl.Paint -= ControlPaint;
                focusedControl.Invalidate();
            }
            focusedControl = sender as Control;
            focusedControl.Paint += ControlPaint;
            if (FocusedControlChanged != null)
                FocusedControlChanged(focusedControl);
        }
        public event Action<Control> FocusedControlChanged;

        void ControlPaint(object sender, PaintEventArgs e)
        {
            if (focusedControl.Size == new Size(e.ClipRectangle.Width, e.ClipRectangle.Height))
            {
                var pen = new Pen(Brushes.Blue, 2);
                e.Graphics.DrawRectangle(pen, e.ClipRectangle);
            }
        }

        private void SetBaseEdit(IEnumerable<Control> controls)
        {
            var editors = controls.Where(p => p is BaseEdit).ToList();
            foreach (BaseEdit control in editors)
            {
                control.ErrorIconAlignment = ErrorIconAlignment.TopRight;
                control.EnterMoveNextControl = true;

                #region 输入控件在输入DEL键时,认为是删除数据
                control.KeyDown += control_KeyDown;
                #endregion

                if (control.DataBindings.Count == 0) continue;
                var binding = control.DataBindings.Cast<Binding>().FirstOrDefault(p => p.PropertyName == "EditValue");
                if (binding == null) continue;
                var bindingSource = binding.DataSource as BindingSource;
                if (bindingSource == null) continue;
                var type = BindingHelper.GetListItemType(bindingSource);
                #region 数据输入控件
                if (type.GetInterface(typeof(IDataErrorInfo).FullName) != null)
                {
                    if (type.GetInterface(typeof(Inaction.Business.IEntity).FullName) != null)
                    {
                        var classInfo = ClassInfo.LoadInfo(type);
                        if (classInfo.NameProperties.ContainsKey(binding.BindingMemberInfo.BindingField))
                        {
                            var propertyInfo = classInfo.NameProperties[binding.BindingMemberInfo.BindingField];
                            SetEditor(control as BaseEdit, propertyInfo, bindingSource);
                        }
                        if (BarManager != null && BarManager.BindingInfos.Any(p => p.BindingSource == bindingSource))
                        {
                            control.GotFocus += control_GotFocus;

                            if (!BindingEditors.ContainsKey(bindingSource))
                            {
                                BindingEditors.Add(bindingSource, new SortedEditorList());
                            }
                            var controlList = BindingEditors[bindingSource];
                            if (!controlList.ContainsValue(control))
                            {
                                if (controlList.ContainsKey(control.TabIndex))
                                {
                                    throw new System.Exception("在同一数据源下,控件:" + control.Name + "的TabIndex值(" + control.TabIndex + ")已存在");
                                }
                                controlList.Add(control.TabIndex, control);
                            }
                        }
                    }
                }
                #endregion

                #region 绑定数据输入控件
                if (type.IsSubclassOf(typeof(Criteria.CriteriaBase)))
                {
                    if (!CriteriaBindingEditors.ContainsKey(bindingSource))
                    {
                        CriteriaBindingEditors.Add(bindingSource, new SortedEditorList());
                    }
                    var controlList = CriteriaBindingEditors[bindingSource];
                    if (!controlList.ContainsValue(control))
                    {
                        controlList.Add(control.TabIndex, control);
                    }
                }
                #endregion

                control.GotFocus += ControlGotFocus;
            }
        }

        void control_KeyDown(object sender, KeyEventArgs e)
        {
            var editor = sender as BaseEdit;
            if (!editor.Properties.ReadOnly && editor.Enabled && e.KeyData == Keys.Delete)
            {
                editor.EditValue = null;
            }
        }

        void control_GotFocus(object sender, System.EventArgs e)
        {
            var control = sender as BaseEdit;
            var binding = control.DataBindings.Cast<Binding>().FirstOrDefault(p => p.PropertyName == "EditValue");
            if (binding == null) return;
            var bindingSource = binding.DataSource as BindingSource;
            if (bindingSource == null) return;
            if (BarManager.CurrentBindingInfo == null || BarManager.CurrentBindingInfo.BindingSource != bindingSource)
            {
                BarManager.CurrentBindingInfo = BarManager.BindingInfos.FirstOrDefault(p => p.BindingSource == bindingSource);
            }
            if (control.Properties.ReadOnly == false && BarManager.EditMode)
            {
                control.Properties.ReadOnly = !BarManager.CurrentBindingInfo.CurrentBusiness.CanSetProperty(binding.BindingMemberInfo.BindingMember);
            }

            if (focusControl != null)
            {
                if (focusControl.Properties.ReadOnly) return;
                var binding1 = focusControl.DataBindings.Cast<Binding>().FirstOrDefault(p => p.PropertyName == "EditValue");
                if (binding1 == null) return;
                var entity = focusControl.BindingManager.Current as IDataErrorInfo;
                if (entity == null) return;
                focusControl.ErrorText = entity[binding1.BindingMemberInfo.BindingField];
                if (!string.IsNullOrEmpty(focusControl.ErrorText))
                {
                    BarManager.ShowMessage(focusControl.ErrorText);
                }
            }
            focusControl = control;
        }

        private void SetEditor(BaseEdit control, IProperty propertyInfo, BindingSource bindingSource)
        {
            control.DataBindings.DefaultDataSourceUpdateMode = DataSourceUpdateMode.OnValidation;

            #region 设置输入控件
            if (control is TextEdit)
            {
                var stringLen = propertyInfo.Column.Length;
                StringLengthAttribute lenght = (StringLengthAttribute)propertyInfo.ValidateAttributes.FirstOrDefault(p => p is StringLengthAttribute);

                TextEdit spinEdit = new TextEdit();
                if (lenght != null)
                {
                    spinEdit.Properties.MaxLength =
                        (lenght.MaximumLength > stringLen) ? lenght.MaximumLength : stringLen.Value;
                }
                else
                {
                    spinEdit.Properties.MaxLength = stringLen.Value;
                }
            }
            if (control is DevExpress.XtraEditors.SpinEdit)
            {
                var floatRange = (RangeAttribute)propertyInfo.ValidateAttributes.FirstOrDefault(p => p is RangeAttribute);
                if (floatRange != null)
                {
                    SpinEdit spinEdit = control as DevExpress.XtraEditors.SpinEdit;
                    spinEdit.Properties.MaxValue = decimal.Parse(floatRange.Maximum.ToString());
                    spinEdit.Properties.MinValue = decimal.Parse(floatRange.Minimum.ToString());
                }
            }
            if (control is DevExpress.XtraEditors.DateEdit)
            {
                var dateType = (DataTypeAttribute)propertyInfo.ValidateAttributes.FirstOrDefault(p => p is DataTypeAttribute);
                if (dateType != null)
                {
                    DateEdit dateEdit = control as DevExpress.XtraEditors.DateEdit;
                    switch (dateType.DataType)
                    {
                        case DataType.Date:
                            dateEdit.Properties.EditMask = "d";
                            break;
                        case DataType.DateTime:
                            dateEdit.Properties.EditMask = "G";
                            break;
                    }
                }
            }
            if (control.Parent is LayoutControl)
            {
                var layout = control.Parent as LayoutControl;
                if (layout == null) return;
                var layoutItem = layout.GetItemByControl(control);
                if (layoutItem == null) return;
                if (!propertyInfo.Column.Nullable)
                {
                    layoutItem.AppearanceItemCaption.Options.UseForeColor = true;
                    layoutItem.AppearanceItemCaption.ForeColor = Color.Blue;
                }
            }
            #endregion
        }


        List<string> NotAllowGetControlNames = new List<string>();

        internal void SetEditorsReadOnly(BindingInfo bindingInfo, bool readOnly)
        {
            if (Form.IsDisposed) return;
            var bindingSource = bindingInfo.BindingSource;
            if (bindingSource == null || !BindingEditors.ContainsKey(bindingSource)) return;
            var editors = BindingEditors[bindingSource];
            var classInfo = ClassInfo.LoadInfo(bindingInfo.ItemType);
            
            var entity = bindingSource.Current as Business.IEntity;

            foreach (BaseEdit edit in editors.Values)
            {
                if (NotAllowGetControlNames.Contains(edit.Name))
                {
                    edit.Properties.ReadOnly = true;
                    continue;
                }
                edit.Properties.ReadOnly = readOnly;
                if (readOnly == true) continue;

                if (entity == null)
                {
                    edit.Properties.ReadOnly = true;
                    continue;
                }

                var binding = bindingSource.CurrencyManager.Bindings.Cast<Binding>().FirstOrDefault(p => p.Control == edit);

                IProperty property = null;
                if (classInfo.NameProperties.ContainsKey(binding.BindingMemberInfo.BindingMember))
                {
                    property = classInfo.NameProperties[binding.BindingMemberInfo.BindingMember];
                }
                if (property != null)
                {
                    if (edit.Properties.ReadOnly == true) continue;
                    edit.Properties.ReadOnly = !entity.CanSetProperty(property.Name);
                    if (edit.Properties.ReadOnly == true) continue;

                    edit.Properties.ReadOnly = property.PropertyInfo.GetSetMethod() == null;//对外部只读的属性也不可写
                }
                else
                {
                    var p = classInfo.EntityType.GetProperty(binding.BindingMemberInfo.BindingMember, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
                    edit.Properties.ReadOnly = property.PropertyInfo.GetSetMethod() == null;//对外部只读的属性也不可写
                }
            }
        }
        #endregion
    }
    public class SortedEditorList : SortedList<int, BaseEdit>
    {
        private BaseEdit firstEditor;
        public BaseEdit FirstEditor
        {
            get
            {
                if (firstEditor == null)
                {
                    firstEditor = this.FirstOrDefault().Value;
                }
                return firstEditor;
            }
        }

        private BaseEdit lastEditor;
        public BaseEdit LastEditor
        {
            get
            {
                if (lastEditor == null)
                {
                    lastEditor = this.LastOrDefault().Value;
                }
                return lastEditor;
            }
        }
    }
}
